#pragma once

#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>

namespace Prod_Cons_Mod
{
    template <typename T>
    class Block_Queue
    {
    private:
        bool IsFull()
        {
            return _blockqueue.size() == _capacity;
        }
        bool IsEmpty()
        {
            return _blockqueue.empty();
        }

    public:
        // 构造函数
        Block_Queue(int capacity)
            : _capacity(capacity)
        {
            _prodwaitnum = 0;
            _conswaitnum = 0;
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_prodcond, nullptr);
            pthread_cond_init(&_conscond, nullptr);
        }
        // 析构函数
        ~Block_Queue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_prodcond);
            pthread_cond_destroy(&_conscond);
        }
        // 拷贝构造
        Block_Queue(const Block_Queue<T> &cpy) = delete;
        // 赋值重载
        Block_Queue operator=(const Block_Queue<T> &cpy) = delete;

        // 生产
        void Push(T *pt)
        {
            // 1.竞争锁
            pthread_mutex_lock(&_mutex);
            // 2.循环判断队列是否放满
            while (IsFull())
            {
                // 等待挂起
                _prodwaitnum++;
                pthread_cond_wait(&_prodcond, &_mutex);
                _prodwaitnum--;
            }
            // 3.入队列
            _blockqueue.push(*pt);
            // 4.唤醒消费者
            if (_conswaitnum > 0)
            {
                pthread_cond_broadcast(&_conscond);
            }
            // 5.释放锁
            pthread_mutex_unlock(&_mutex);
        }
        // 消费
        T Pop()
        {
            //此处要求传自定义类型的时候需要提供默认构造
            T t;
            // 1.竞争锁
            pthread_mutex_lock(&_mutex);
            // 2.循环判断队列是否为空
            while (IsEmpty())
            {
                // 等待挂起
                _conswaitnum++;
                pthread_cond_wait(&_conscond, &_mutex);
                _conswaitnum--;
            }
            // 3.出队列
            //此处要求传自定义类型的时候需要提供赋值重载
            t = _blockqueue.front();
            _blockqueue.pop();
            // 4.唤醒生产者
            if (_prodwaitnum > 0)
            {
                pthread_cond_broadcast(&_prodcond);
            }
            // 5.释放锁
            pthread_mutex_unlock(&_mutex);
            return t;
        }

    private:
        std::queue<T> _blockqueue; // 阻塞队列
        int _capacity;               // 容量

        pthread_mutex_t _mutex;   // 互斥量
        pthread_cond_t _prodcond; // 生产者条件变量
        pthread_cond_t _conscond; // 消费者条件变量

        int _prodwaitnum; // 生产者等待数量
        int _conswaitnum; // 消费者等待数量
    };
}